///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __Frustum_H__
#define __Frustum_H__

#include "OgrePrerequisites.h"
#include "OgreMovableObject.h"
#include "OgreRenderable.h"
#include "math/axisAlignedBox.h"
#include "OgreVertexIndexData.h"
#include "OgreMovablePlane.h"

namespace Ogre
{
	 
    enum OrientationMode
    {
        OR_DEGREE_0       = 0,
        OR_DEGREE_90      = 1,
        OR_DEGREE_180     = 2,
        OR_DEGREE_270     = 3,

        OR_PORTRAIT       = OR_DEGREE_0,
        OR_LANDSCAPERIGHT = OR_DEGREE_90,
        OR_LANDSCAPELEFT  = OR_DEGREE_270
    };

	
    enum ProjectionType
    {
        PT_ORTHOGRAPHIC,
        PT_PERSPECTIVE
    };

    
    enum FrustumPlane
    {
        FRUSTUM_PLANE_NEAR   = 0,
        FRUSTUM_PLANE_FAR    = 1,
        FRUSTUM_PLANE_LEFT   = 2,
        FRUSTUM_PLANE_RIGHT  = 3,
        FRUSTUM_PLANE_TOP    = 4,
        FRUSTUM_PLANE_BOTTOM = 5
    };

    
    class _OgreExport Frustum : public MovableObject, public Renderable
    {
    protected:
        /// Orthographic or perspective?
        ProjectionType mProjType;

        /// y-direction field-of-view (default 45)
        Radian mFOVy;
        /// Far clip distance - default 10000
        Real mFarDist;
        /// Near clip distance - default 100
        Real mNearDist;
        /// x/y viewport ratio - default 1.3333
        Real mAspect;
		/// Ortho height size (world units)
		Real mOrthoHeight;
        /// Off-axis frustum center offset - default (0.0, 0.0)
        Vector2 mFrustumOffset;
        /// Focal length of frustum (for stereo rendering, defaults to 1.0)
        Real mFocalLength;

        /// The 6 main clipping planes
        mutable Plane mFrustumPlanes[6];

        /// Stored versions of parent orientation / position
        mutable Quaternion mLastParentOrientation;
        mutable Vector3 mLastParentPosition;

        /// Pre-calced projection matrix for the specific render system
        mutable Matrix4 mProjMatrixRS;
        /// Pre-calced standard projection matrix but with render system depth range
        mutable Matrix4 mProjMatrixRSDepth;
        /// Pre-calced standard projection matrix
        mutable Matrix4 mProjMatrix;
        /// Pre-calced view matrix
        mutable Matrix4 mViewMatrix;
        /// Something's changed in the frustum shape?
        mutable bool mRecalcFrustum;
        /// Something re the view pos has changed
        mutable bool mRecalcView;
        /// Something re the frustum planes has changed
        mutable bool mRecalcFrustumPlanes;
        /// Something re the world space corners has changed
        mutable bool mRecalcWorldSpaceCorners;
        /// Something re the vertex data has changed
        mutable bool mRecalcVertexData;
		/// Are we using a custom view matrix?
		bool mCustomViewMatrix;
		/// Are we using a custom projection matrix?
		bool mCustomProjMatrix;
		/// Have the frustum extents been manually set?
		bool mFrustumExtentsManuallySet;
		/// Frustum extents
		mutable Real mLeft, mRight, mTop, mBottom;
        /// Frustum orientation mode
        mutable OrientationMode mOrientationMode;
		
        // Internal functions for calcs
        virtual void calcProjectionParameters(Real& left, Real& right, Real& bottom, Real& top) const;
		/// Update frustum if out of date
        virtual void updateFrustum(void) const;
		/// Update view if out of date
        virtual void updateView(void) const;
		/// Implementation of updateFrustum (called if out of date)
		virtual void updateFrustumImpl(void) const;
		/// Implementation of updateView (called if out of date)
		virtual void updateViewImpl(void) const;
        virtual void updateFrustumPlanes(void) const;
		/// Implementation of updateFrustumPlanes (called if out of date)
		virtual void updateFrustumPlanesImpl(void) const;
        virtual void updateWorldSpaceCorners(void) const;
		/// Implementation of updateWorldSpaceCorners (called if out of date)
		virtual void updateWorldSpaceCornersImpl(void) const;
        virtual void updateVertexData(void) const;
        virtual bool isViewOutOfDate(void) const;
        virtual bool isFrustumOutOfDate(void) const;
        /// Signal to update frustum information.
        virtual void invalidateFrustum(void) const;
        /// Signal to update view information.
        virtual void invalidateView(void) const;

        /// Shared class-level name for Movable type
        static String msMovableType;

        mutable AxisAlignedBox mBoundingBox;
        mutable VertexData mVertexData;

        MaterialPtr mMaterial;
        mutable Vector3 mWorldSpaceCorners[8];

        /// Is this frustum to act as a reflection of itself?
        bool mReflect;
		/// Derived reflection matrix
        mutable Matrix4 mReflectMatrix;
        /// Fixed reflection plane
		mutable Plane mReflectPlane;
		/// Pointer to a reflection plane (automatically updated)
		const MovablePlane* mLinkedReflectPlane;
		/// Record of the last world-space reflection plane info used
		mutable Plane mLastLinkedReflectionPlane;
		
        /// Is this frustum using an oblique depth projection?
		bool mObliqueDepthProjection;
		/// Fixed oblique projection plane
		mutable Plane mObliqueProjPlane;
		/// Pointer to oblique projection plane (automatically updated)
		const MovablePlane* mLinkedObliqueProjPlane;
		/// Record of the last world-space oblique depth projection plane info used
		mutable Plane mLastLinkedObliqueProjPlane;

    public:

		/// Named constructor
		Frustum(const String& name = StringUtil::BLANK);

        virtual ~Frustum();
        
        virtual void setFOVy(const Radian& fovy);

        
        virtual const Radian& getFOVy(void) const;

        
        virtual void setNearClipDistance(Real nearDist);

        
        virtual Real getNearClipDistance(void) const;

        
        virtual void setFarClipDistance(Real farDist);

        
        virtual Real getFarClipDistance(void) const;

        
        virtual void setAspectRatio(Real ratio);

        
        virtual Real getAspectRatio(void) const;

        
        virtual void setFrustumOffset(const Vector2& offset);

        
        virtual void setFrustumOffset(Real horizontal = 0.0, Real vertical = 0.0);

        
        virtual const Vector2& getFrustumOffset() const;

        
        virtual void setFocalLength(Real focalLength = 1.0);

        
        virtual Real getFocalLength() const;

		
		virtual void setFrustumExtents(Real left, Real right, Real top, Real bottom);
		
		virtual void resetFrustumExtents(); 
		
		virtual void getFrustumExtents(Real& outleft, Real& outright, Real& outtop, Real& outbottom) const;


        
        virtual const Matrix4& getProjectionMatrixRS(void) const;
        
        virtual const Matrix4& getProjectionMatrixWithRSDepth(void) const;
        
        virtual const Matrix4& getProjectionMatrix(void) const;

        
        virtual const Matrix4& getViewMatrix(void) const;

		
		virtual void calcViewMatrixRelative(const Vector3& relPos, Matrix4& matToUpdate) const;

		
		virtual void setCustomViewMatrix(bool enable, 
			const Matrix4& viewMatrix = Matrix4::IDENTITY);
		/// Returns whether a custom view matrix is in use
		virtual bool isCustomViewMatrixEnabled(void) const 
		{ return mCustomViewMatrix; }
		
		
		virtual void setCustomProjectionMatrix(bool enable, 
			const Matrix4& projectionMatrix = Matrix4::IDENTITY);
		/// Returns whether a custom projection matrix is in use
		virtual bool isCustomProjectionMatrixEnabled(void) const
		{ return mCustomProjMatrix; }

		
        virtual const Plane* getFrustumPlanes(void) const;

        
        virtual const Plane& getFrustumPlane( unsigned short plane ) const;

        
        virtual bool isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy = 0) const;

        
        virtual bool isVisible(const Sphere& bound, FrustumPlane* culledBy = 0) const;

        
        virtual bool isVisible(const Vector3& vert, FrustumPlane* culledBy = 0) const;

		/// Overridden from MovableObject::getTypeFlags
		uint32 getTypeFlags(void) const;

        
        const AxisAlignedBox& getBoundingBox(void) const;

        
		Real getBoundingRadius(void) const;

		
        void _updateRenderQueue(RenderQueue* queue);

        
        const String& getMovableType(void) const;

        
        void _notifyCurrentCamera(Camera* cam);

        
        const MaterialPtr& getMaterial(void) const;

        
        void getRenderOperation(RenderOperation& op);

        
        void getWorldTransforms(Matrix4* xform) const;

        
        Real getSquaredViewDepth(const Camera* cam) const;

        
        const LightList& getLights(void) const;

        
        virtual const Vector3* getWorldSpaceCorners(void) const;

        
        virtual void setProjectionType(ProjectionType pt);

        
        virtual ProjectionType getProjectionType(void) const;

		
		virtual void setOrthoWindow(Real w, Real h);
		
		virtual void setOrthoWindowHeight(Real h);
		
		virtual void setOrthoWindowWidth(Real w);
		
		virtual Real getOrthoWindowHeight() const;
		
		virtual Real getOrthoWindowWidth() const;

        
        virtual void enableReflection(const Plane& p);
        
        virtual void enableReflection(const MovablePlane* p);

        
        virtual void disableReflection(void);

        /// Returns whether this frustum is being reflected
        virtual bool isReflected(void) const { return mReflect; }
        /// Returns the reflection matrix of the frustum if appropriate
        virtual const Matrix4& getReflectionMatrix(void) const { return mReflectMatrix; }
        /// Returns the reflection plane of the frustum if appropriate
        virtual const Plane& getReflectionPlane(void) const { return mReflectPlane; }

        
        virtual bool projectSphere(const Sphere& sphere, 
            Real* left, Real* top, Real* right, Real* bottom) const;


		
		virtual void enableCustomNearClipPlane(const MovablePlane* plane);
		
		virtual void enableCustomNearClipPlane(const Plane& plane);
		
		virtual void disableCustomNearClipPlane(void);
		
		virtual bool isCustomNearClipPlaneEnabled(void) const 
		{ return mObliqueDepthProjection; }

		/// @copydoc MovableObject::visitRenderables
		void visitRenderables(Renderable::Visitor* visitor, bool debugRenderables = false);

        /// Small constant used to reduce far plane projection to avoid inaccuracies
        static const Real INFINITE_FAR_PLANE_ADJUST;

		
		virtual const Vector3& getPositionForViewUpdate(void) const;
		
		virtual const Quaternion& getOrientationForViewUpdate(void) const;

		
		PlaneBoundedVolume getPlaneBoundedVolume();
        
        void setOrientationMode(OrientationMode orientationMode);

        
        OrientationMode getOrientationMode() const;

    };

	
}

#endif 
